package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.gulimall.common.exception.NoStockException;
import com.atguigu.gulimall.common.mq.StockDetailTo;
import com.atguigu.gulimall.common.mq.StockLockedTo;
import com.atguigu.gulimall.common.to.OrderTo;
import com.atguigu.gulimall.common.utils.PageUtils;
import com.atguigu.gulimall.common.utils.Query;
import com.atguigu.gulimall.common.utils.R;
import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.service.WareSkuService;
import com.atguigu.gulimall.ware.vo.OrderItemVo;
import com.atguigu.gulimall.ware.vo.OrderVo;
import com.atguigu.gulimall.ware.vo.SkuHasStockVo;
import com.atguigu.gulimall.ware.vo.WareSkuLockVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@RabbitListener(queues = "stock.release.stock.queue")
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    private WareOrderTaskService orderTaskService;

    @Autowired
    private WareOrderTaskDetailService orderTaskDetailService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderFeignService orderFeignService;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    private void unLockStock(Long skuId, Long wareId, Integer skuNum, Long detailId) {
        wareSkuDao.unLockStock(skuId, wareId, skuNum);
        // 更新库存追溯工作单详情的状态
        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
        detailEntity.setId(detailId);
        detailEntity.setLockStatus(2);
        orderTaskDetailService.updateById(detailEntity);
    }

    /**
     * 防止订单服务卡顿，导致订单状态消,息一直改不了，库存消,息优先到期。查订单状态新建状态，什么都不做就走了。
     * 导致卡顿的订单，永远不能解锁库存
     * @param orderTo
     */
    @Transactional
    @Override
    public void unLockStock(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();
        // 查询库存回溯工作单和工作单详情，找到需要解锁的库存，解锁
        WareOrderTaskEntity task = orderTaskService.getOrderTaskByOrderSn(orderSn);
        Long taskId = task.getId();
        List<WareOrderTaskDetailEntity> list = orderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", taskId).eq("lock_status", 1));
        if (list != null && list.size() > 0){
            for (WareOrderTaskDetailEntity detailEntity : list) {
                unLockStock(detailEntity.getSkuId(), detailEntity.getWareId(), detailEntity.getSkuNum(), detailEntity.getId());
            }
        }

    }

    @Override
    public void unLockStock(StockLockedTo lockedTo) {
        Long id = lockedTo.getId();
        StockDetailTo detail = lockedTo.getDetailTo();
        Long detailId = detail.getId();
        /**
         * 解锁：
         *  1. 查询数据库关于这个订单的锁库存信息
         *      有
         *      没有 :  锁库存失败，库存回滚了，这种情况无需解锁
         */
        WareOrderTaskDetailEntity detailEntity = orderTaskDetailService.getById(detail.getId());
        if (detailEntity != null){
            // 解锁
            WareOrderTaskEntity taskEntity = orderTaskService.getById(id);
            R r = orderFeignService.getOrderByOrderSn(taskEntity.getOrderSn());
            if (r.getCode() == 0){
                // 订单数据返回成功
                OrderVo data = r.getData(new TypeReference<OrderVo>() {
                });
                if (data == null || data.getStatus() == 4){
                    // 订单已经被取消， 才能释放库存
                    if (detailEntity.getLockStatus() == 1){
                        unLockStock(detailEntity.getSkuId(), detail.getWareId(), detail.getSkuNum(), detailId);
                    }
                }
            }else {
                throw new RuntimeException("解锁库存失败-远程查询订单状态失败...");
            }
        }
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String skuId = (String) params.get("skuId");
        if (StringUtils.isNotBlank(skuId) && !"0".equals(skuId)){
            queryWrapper.eq("sku_id", skuId);
        }

        String wareId = (String) params.get("wareId");
        if (StringUtils.isNotBlank(wareId) && !"0".equals(wareId)){
            queryWrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(new Query<WareSkuEntity>().getPage(params),queryWrapper);

        return new PageUtils(page);
    }



    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {

        List<WareSkuEntity> wareSkuEntityList = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (wareSkuEntityList == null || wareSkuEntityList.size() == 0){
            WareSkuEntity entity = new WareSkuEntity();
            entity.setSkuId(skuId);
            entity.setWareId(wareId);
            entity.setStock(skuNum);
            entity.setStockLocked(0);
            // 调用远程商品服务，查询sku名称， 如果失败，整个事务无需回滚
            // 为了个skuName查询出错进行回滚不值得，这里先try/catch，后续高级课程再处理
            try {
                R result = productFeignService.info(skuId);
                Map<String, Object> skuInfoMap = (Map<String, Object>) result.get("skuInfo");
                if (result.getCode() == 0){
                    entity.setSkuName((String) skuInfoMap.get("skuName"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            wareSkuDao.insert(entity);
        }else {
            wareSkuDao.addStock(skuId, wareId, skuNum);
        }
    }

    @Override
    public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {

        List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
            SkuHasStockVo vo = new SkuHasStockVo();
            // SELECT SUM(stock - stock_locked) FROM wms_ware_sku where sku_id = 1
            Long count = this.baseMapper.getSkuStock(skuId);
            vo.setSkuId(skuId);
            vo.setHasStock(count != null && count > 0);
            return vo;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 为某个订单锁定库存
     * @param vo
     * @return
     */
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {

        // 保存库存追溯工作单
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        orderTaskService.save(taskEntity);


        // 1. 按照下单的收货地址，就近选择一个仓库，锁定库存（不做）
        // 1. 找到每个商品都在哪个仓库有库存
        List<OrderItemVo> locks = vo.getLocks();
        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);
            stock.setNum(item.getCount());
            // 查询这个商品在哪个仓库有库存
            List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
            stock.setWareId(wareIds);
            return stock;
        }).collect(Collectors.toList());

        // 2. 锁定库存
        for (SkuWareHasStock stock : collect) {
            Long skuId = stock.getSkuId();
            Integer num = stock.getNum();
            List<Long> wareIds = stock.getWareId();
            if (wareIds == null || wareIds.size() == 0){
                // 没有任何仓库有这个商品的库存
                throw new NoStockException(skuId);
            }
            boolean skuStocked = false;
            for (Long wareId : wareIds) {
                // 成功返回1， 失败返回0
                Long count = wareSkuDao.lockSkuStock(skuId, wareId, num);
                if (count == 1){
                    // 锁库成功
                    skuStocked = true;
                    // 告诉MQ锁库存成功
                    WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
                    detailEntity.setSkuId(skuId);
                    detailEntity.setSkuNum(num);
                    detailEntity.setWareId(wareId);
                    detailEntity.setTaskId(taskEntity.getId());
                    detailEntity.setLockStatus(1);
                    orderTaskDetailService.save(detailEntity);
                    StockLockedTo lockedTo = new StockLockedTo();
                    StockDetailTo detailTo = new StockDetailTo();
                    BeanUtils.copyProperties(detailEntity, detailTo);
                    lockedTo.setId(taskEntity.getId());
                    lockedTo.setDetailTo(detailTo);
                    rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", lockedTo);



                    break;
                }else {
                    // 当前仓库锁库存失败， 尝试下一个仓库
                }
            }
            if (!skuStocked){ // 所有仓库锁库存失败
                throw new NoStockException(skuId);

            }
        }

        return true;
    }

    @Data
    class SkuWareHasStock{
        private Long skuId;
        private List<Long> wareId;
        private Integer num;
    }
}